package com.duoduo.common.api.help;


import com.duoduo.common.api.constants.LogConstant;
import com.duoduo.common.api.tuple.Tuple;
import com.duoduo.common.api.tuple.TwoTuple;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.ResolvableType;

import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.reflect.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**反射帮助类
 * @author duoduo
 *
 */
public class ReflectionInnerUtil {

	private static final Logger LOGGER = LoggerFactory.getLogger(ReflectionInnerUtil.class);
	
	/**创建指定类型的数组，返回数组类对象
	 * @param clazz
	 * @param size
	 * @return
	 */
	@SuppressWarnings("rawtypes")
	public static Class createArray(Class clazz, int size) {
		return Array.newInstance(clazz, size).getClass();
	}
	
	/**判断一个对象是否是基本类型或基本类型的封装类型
	 * @param obj
	 * @return
	 */
	@SuppressWarnings("rawtypes")
	public static boolean isPrimitive(Object obj) {
		Field f = null;
		try {
			f = obj.getClass().getField("TYPE");
		} catch (Exception e) {
			return false;
		}
		if(f == null){
			return false;
		}
		Class c = null;
		try {
			c = (Class<?>)f.get(null);
		} catch (Exception e) {
			return false;
		}
		if(c == null){
			return false;
		}
		return c.isPrimitive();
	}
	
	/**调用带参数构造方法
	 * @param clazz
	 * @param params
	 * @return
	 */
	@SuppressWarnings("rawtypes")
	public static Object invokeConstructor(Class<?> clazz, Class[] paramClasses, Object[] params) {
		if(clazz == null){
			return null;
		}
		try{
			Constructor<?> constructor = clazz.getConstructor(paramClasses);
			if(constructor == null){
				return null;
			}
			return constructor.newInstance(params);
		}catch(Exception e){
			LOGGER.error(LogConstant.LOADER_CLASS + " invokeConstructor error: ", e);
		}
		return null;
	}
	
	/**调用无参数构造方法
	 * @param clazz
	 * @return
	 */
	public static Object invokeConstructor(Class<?> clazz) {
		if(clazz == null){
			return null;
		}
		try{
			return clazz.newInstance();
		}catch(Exception e){
			LOGGER.error(LogConstant.LOADER_CLASS + " invokeConstructor error: ", e);
		}
		return null;
	}
	
	@SuppressWarnings("rawtypes")
	public static Object invokeStaticMethod(Class<?> clazz, String methodName, Class[] paramClasses, Object[] params) throws Throwable {
		if(clazz == null || methodName == null){
			return null;
		}
		try{
			Method method = clazz.getMethod(methodName, paramClasses);
			if(method == null){
				return null;
			}
			method.setAccessible(true);
			return method.invoke(null, params);
		}catch(Exception e){
			LOGGER.error(LogConstant.LOADER_CLASS + " invokeStaticMethod error: ", e);
			throw e.getCause();
		}
	}
	
	public static Object invokeStaticMethod(Class<?> clazz, String methodName) throws Throwable {
		if(clazz == null || methodName == null){
			return null;
		}
		try{
			Method method = clazz.getMethod(methodName);
			if(method == null){
				return null;
			}
			method.setAccessible(true);
			return method.invoke(null);
		}catch(Exception e){
			LOGGER.error(LogConstant.LOADER_CLASS + " invokeStaticMethod error: ", e);
			throw e.getCause();
		}
	}
	
	@SuppressWarnings("rawtypes")
	public static Object invokeMethod(Object obj, String methodName, Class[] paramClasses, Object[] params) throws Throwable {
		if(obj == null || methodName == null){
			return null;
		}
		try{
			Method method = obj.getClass().getMethod(methodName, paramClasses);
			if(method == null){
				return null;
			}
			method.setAccessible(true);
			return method.invoke(obj, params);
		}catch(Exception e){
			LOGGER.error(LogConstant.LOADER_CLASS + " invokeMethod error: ", e);
			throw e.getCause();
		}
	}
	
	@SuppressWarnings("rawtypes")
	public static Object invokeDeclaredMethod(Object obj, String methodName, Class[] paramClasses, Object[] params) throws Throwable {
		if(obj == null || methodName == null){
			return null;
		}
		try{
			Method method = obj.getClass().getDeclaredMethod(methodName, paramClasses);
			if(method == null){
				return null;
			}
			method.setAccessible(true);
			return method.invoke(obj, params);
		}catch(Exception e){
			LOGGER.error(LogConstant.LOADER_CLASS + " invokeMethod error: ", e);
			throw e.getCause();
		}
	}
	
	public static Object invokeMethod(Object obj, String methodName) throws Throwable {
		if(obj == null || methodName == null){
			return null;
		}
		try{
			Method method = obj.getClass().getMethod(methodName);
			if(method == null){
				return null;
			}
			method.setAccessible(true);
			return method.invoke(obj);
		}catch(Exception e){
			LOGGER.error(LogConstant.LOADER_CLASS + " invokeMethod error: ", e);
			throw e.getCause();
		}
	}
	
	public static Object invokeDeclaredMethod(Object obj, String methodName) throws Throwable {
		if(obj == null || methodName == null){
			return null;
		}
		try{
			Method method = obj.getClass().getDeclaredMethod(methodName);
			if(method == null){
				return null;
			}
			method.setAccessible(true);
			return method.invoke(obj);
		}catch(Exception e){
			LOGGER.error(LogConstant.LOADER_CLASS + " invokeMethod error: ", e);
			throw e.getCause();
		}
	}

	/**实例化enum
	 * @param enumClass
	 * @param enumString
	 * @return
	 */
	@SuppressWarnings({ "rawtypes", "unchecked" })
	public static Object instanceEnum(Class enumClass, String enumString) {
		return Enum.valueOf(enumClass, enumString);
	}

	/**获取静态属性值
	 * @param clazz
	 * @param fieldName
	 * @return
	 */
	public static Object getStaticFieldValue(Class<?> clazz, String fieldName) {
		Field[] fields = clazz.getDeclaredFields();
        try {
            for (Field field : fields) {
                field.setAccessible(true);
                if(Modifier.isStatic(field.getModifiers()) && field.getName().equals(fieldName)){
                	return field.get(clazz);
                }
            }
        } catch (Exception e) {
        	LOGGER.error(LogConstant.LOADER_CLASS + " getStaticFieldValue error: ", e);
        }
        return null;
	}
	
	public static Object getFieldValue(Object obj, Field field) {
        try {
            field.setAccessible(true);
            return field.get(obj);
        } catch (Exception e) {
        	LOGGER.error(LogConstant.LOADER_CLASS + " getFieldValue error: ", e);
        }
        return null;
	}
	
	@SuppressWarnings("rawtypes")
	public static Object getFieldValue(Object obj, String fieldName) {
		Field field = null;
		Class c = obj.getClass();
		// 尝试从父类查找
		for (; c != Object.class; c = c.getSuperclass()) {//获取本身和父级对象
            try{
    			field = c.getDeclaredField(fieldName);
    		}catch(Exception e){
    			LOGGER.warn(LogConstant.LOADER_CLASS + " getField: {}", fieldName);
    		}
            if(field != null){
            	break;
            }
        }
		if(field == null){
			return null;
		}
		try {
			field.setAccessible(true);
			return field.get(obj);
		} catch (Exception e) {
			LOGGER.error(LogConstant.LOADER_CLASS + " getFieldValue error: ", e);
		}
		return null;
	}
	
	public static void setFieldValue(Object obj, Field field, Object value) {
		try {
			field.setAccessible(true);
			field.set(obj, value);
		} catch (Exception e) {
			LOGGER.error(LogConstant.LOADER_CLASS + " setFieldValue error: ", e);
		}
	}
	
	@SuppressWarnings("rawtypes")
	public static void setFieldValue(Object obj, String fieldName, Object value) {
		Field field = null;
		Class c = obj.getClass();
		// 尝试从父类查找
		for (; c != Object.class; c = c.getSuperclass()) {//获取本身和父级对象
            try{
    			field = c.getDeclaredField(fieldName);
    		}catch(Exception e){
    			LOGGER.warn(LogConstant.LOADER_CLASS + " getField: {}", fieldName);
    		}
            if(field != null){
            	break;
            }
        }
		if(field == null){
			return;
		}
		try {
			field.setAccessible(true);
			field.set(obj, value);
		} catch (Exception e) {
			LOGGER.error(LogConstant.LOADER_CLASS + " setFieldValue error: ", e);
		}
	}
	
	/**获取指定类的所有属性，包括父类的
	 * @param clazz
	 * @return
	 */
	public static Field[] findAllFields(Class<?> clazz) {
		List<Field> fieldList = new ArrayList<>();
		
		for (; clazz != Object.class; clazz = clazz.getSuperclass()) {//获取本身和父级对象
            try{
            	Field[] dFields = clazz.getDeclaredFields();
            	if(ArrayInnerUtil.isNotEmpty(dFields)){
            		fieldList.addAll(Arrays.asList(dFields));
            	}
    		}catch(Exception e){
    			LOGGER.warn(LogConstant.LOADER_CLASS + " findAllFields: {}", clazz.getName());
    		}
        }
		
		Field[] result = new Field[fieldList.size()];
		fieldList.toArray(result);
		return result;
	}

	/**查找类属性上是否有指定注解，有就返回属性值和注解信息
	 * @param clazz
	 * @param annoClass
	 * @return
	 */
	public static <T extends Serializable> TwoTuple<Annotation, Object> getFiledValByAnnotation(T obj, Class<? extends Annotation> annoClass) {
		Class<? extends Serializable> clazz = obj.getClass();
		Field[] fields = clazz.getDeclaredFields();
        try {
            for (Field field : fields) {
                field.setAccessible(true);
                if(field.isAnnotationPresent(annoClass)){
                	return Tuple.tuple(field.getAnnotation(annoClass), field.get(obj));
                }
            }
        } catch (Exception e) {
        	LOGGER.error(LogConstant.LOADER_CLASS + " getStaticFieldValue error: ", e);
        }
        return null;
	}
	
	/**查找类属性上是否有指定注解，有就返回Filed对象和注解信息
	 * @param clazz
	 * @param annoClass
	 * @return
	 */
	public static <T extends Serializable> TwoTuple<Annotation, Field> getFiledInfoByAnnotation(Class<? extends Serializable> clazz, Class<? extends Annotation> annoClass) {
		Field[] fields = clazz.getDeclaredFields();
		try {
			for (Field field : fields) {
				field.setAccessible(true);
				if(field.isAnnotationPresent(annoClass)){
					return Tuple.tuple(field.getAnnotation(annoClass), field);
				}
			}
		} catch (Exception e) {
			LOGGER.error(LogConstant.LOADER_CLASS + " getStaticFieldValue error: ", e);
		}
		return null;
	}

	/**从目标class里获取指定方法
	 * @param targetClass
	 * @param methodName
	 * @param pType
	 * @return
	 */
	public static Method getMethod(Class<?> targetClass, String methodName, Class<?> pType) {
		try {
			return targetClass.getMethod(methodName, pType);
		} catch (NoSuchMethodException | SecurityException e) {
			LOGGER.error(LogConstant.LOADER_CLASS + " getMethod error: ", e);
		}
		return null;
	}
	
	/**获取方法参数上的泛型
	 * @param method
	 * @param parameterIndex 参数索引
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public static Class<? extends Serializable> resolveMethodParameterizedType(Method method, int parameterIndex) {
		try {
			return (Class<? extends Serializable>)ResolvableType.forMethodParameter(method, parameterIndex).getGeneric(parameterIndex).resolve();
		}catch (Exception e) {
			LOGGER.error(LogConstant.LOADER_CLASS + " get method param ParameterizedType error, method: {}, index: {}", method.getName(), parameterIndex);
		}
		return null;
	}
	
	/**方法描述：获取方法的返回值类型，带泛型
	 *
	 * @param joinpoint
	 * @return 如果没有泛型，返回null
	 */
	public static ParameterizedType getMethodGenericReturnType(Method method){
		if(method.getGenericReturnType() instanceof ParameterizedType)
			return (ParameterizedType) method.getGenericReturnType();
		return null;
	}

}
